package com.et.mserver;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

// send message to client
public class PrefixedXMLStreamEncoder extends ProtocolEncoderAdapter {

	public PrefixedXMLStreamEncoder() {
		this.charsetEncoder = Charset.forName(MServerConstants.DEFAULT_CHARSET_NAME).newEncoder();
	}

	@Override
	public void encode(final IoSession session, final Object message, final ProtocolEncoderOutput out) throws Exception {

		if (message == null) {
			return;
		}

		final IoBuffer buffer;

		if (message instanceof Element) {

			final byte[] bytes;

			try {
				bytes = this.elementToBytes((Element) message);
			} catch (IOException e) {
				if (logger.isInfoEnabled()) {
					logger.info("[session " + session.getId() + "] IO failed: ", e);
				}
				throw e;
			}

			if ((bytes == null) || (bytes.length == 0)) {
				buffer = IoBuffer.allocate(MServerConstants.DEFAULT_PREFIX_LENGTH);
				buffer.putInt(0);
			} else {
				buffer = IoBuffer.allocate(MServerConstants.DEFAULT_PREFIX_LENGTH + bytes.length);
				buffer.putInt(bytes.length);
				buffer.put(bytes);
			}
			buffer.flip();

			if (buffer.remaining() > MServerConstants.DEFAULT_MAX_DATA_LENGTH) {
				throw new IllegalArgumentException("[session " + session.getId() + "] Data length: " + buffer.remaining());
			}

		} else {

			final String value = message.toString();

			buffer = IoBuffer.allocate(value.length()).setAutoExpand(true);

			this.charsetEncoder.reset();
			try {
				buffer.putPrefixedString(value, MServerConstants.DEFAULT_PREFIX_LENGTH, this.charsetEncoder);
			} catch (CharacterCodingException e) {

				if (logger.isInfoEnabled()) {
					logger.info("[session " + session.getId() + "] bad charset message: ", e);
				}
				throw e;
			}

			buffer.flip();
			if (buffer.remaining() > MServerConstants.DEFAULT_MAX_DATA_LENGTH) { // 判断消息是否过大
				throw new IllegalArgumentException("[session " + session.getId() + "] Data length: " + buffer.remaining());
			}

		}

		out.write(buffer);

		if (logger.isDebugEnabled()) {
			logger.debug("[session " + session.getId() + "] write message session: " + session.getId());
		}


	}

	private byte[] elementToBytes(Element message) throws IOException {

		Element element = message;

		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		OutputFormat format = new OutputFormat("", false, MServerConstants.DEFAULT_CHARSET_NAME);

		XMLWriter writer = new XMLWriter(bos, format);

		writer.write(element);

		return bos.toByteArray();
	}

	private static final Logger logger = LoggerFactory.getLogger(PrefixedXMLStreamEncoder.class);

	private final CharsetEncoder charsetEncoder;
}
